/*
 * Copyright 2014 Will Dignazio
 */
#include <time.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/un.h>
#include <sys/socket.h>
#include "internal.h"
#include <socketio.h>

#ifndef UNIX_PATH_MAX
  #define UNIX_PATH_MAX 108
#endif

enum
{
	MAX_BACKLOG	 = 500,
};

struct WorkItem
{
	SocketCallback	work;
	SocketSession *session;
	SocketData *sdata;
};

struct WorkQueue
{
	Queue		*queue;
	pthread_t	worker;
	pthread_mutex_t	wlock;
};

WorkQueue	*global_workqueue;

static WorkItem*
allocworkitem(void)
{
	WorkItem *witem;

	witem = malloc(sizeof(*witem));
	if(witem == NULL)
		goto fail;

	witem->work = NULL;
fail:
	return NULL;
}

void*
worker_thread(void *arg)
{
	WorkItem *witem;
	int ret;

	witem = NULL;

	for(;;) {
		/* Attempt to save some cycles by waiting if the queue is empty */
		if(queue_empty(global_workqueue->queue)) {
			pthread_mutex_lock(&global_workqueue->wlock);
			pthread_cond_wait(queue_getsignal(global_workqueue->queue), &global_workqueue->wlock);

			ret = queue_pop(global_workqueue->queue, &witem);
			pthread_mutex_unlock(&global_workqueue->wlock);
		} 
		else
			ret = queue_pop(global_workqueue->queue, &witem);

		/* 
		 * We've gotten a work item, and atomically removed it from the others.
		 * So now let's actually perform the task at hand.
		 */
		if(witem != NULL)
			ret = witem->work(witem->sdata, witem->session);
		else
			continue; // Meh, we wasted some time.
	}

	return NULL;
}

int
work_enqueue(SocketCallback work, SocketData *sdata, SocketSession *session)
{
	WorkItem *witem;
	int ret;

	ret = -1;
	witem = allocworkitem();
	if(witem == NULL)
		goto fail;

	witem->work = work;
	witem->session = session;
	witem->sdata = sdata;

	ret = queue_push(global_workqueue->queue, witem);
	if(ret != QUEUE_SUCCESS) {
		ret = -1;
		goto fail;
	}

	ret = 0;
fail:
	return ret;
}

WorkQueue*
allocworkqueue(void)
{
	WorkQueue *wqueue;

	wqueue = malloc(sizeof(*wqueue));
	if(wqueue == NULL)
		goto fail;

	wqueue->queue = queue_create(0); // Default is fine
	if(wqueue == NULL)
		goto fail;

	pthread_mutex_init(&wqueue->wlock, NULL);
	/* Initialize worker thread for work queue */
	return wqueue;
fail:
	if(wqueue != NULL)
		free(wqueue);
	return NULL;
}

void
freeworkqueue(WorkQueue *wqueue)
{
	if(wqueue == NULL)
		return;

	if(wqueue->queue != NULL)
		queue_free(wqueue->queue);

	free(wqueue);
}
